using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using Poly2Tri;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using PolyDecomp;

namespace GameStateManagementSample.Classes
{
    class Region
    {
        private List<Vector2> polygon;

        VertexPositionColor[] vpc;
        private Color fillColor;
        public Color FillColor
        {
            get { return fillColor; }
            set
            {
                if (vpc != null)
                {
                    for (var i = 0; i < vpc.Count(); i++)
                    {
                        vpc[i].Color = value;
                    }
                }
                fillColor = value;
            }
        }
        public Region(List<Vector2> points, Color col)
        {
            FillColor = col;
            Polygon = points;
        }

        //public bool AddPolygon(List<Vector2> poly)
        //{
        //    var regpoly = new List<Vector2>(Vectors);
        //    if (GeometryHelper.IsClockwisePolygon(regpoly))
        //    {
        //        regpoly.Reverse();
        //    }
        //    var addpoly = new List<Vector2>(poly);
        //    if (GeometryHelper.IsClockwisePolygon(addpoly))
        //    {
        //        addpoly.Reverse();
        //    }
        //    List<Vector2> retUnion, retIntersection;
        //    bool ret = PolygonUnion.PolyUnion(regpoly, addpoly, out retUnion, out retIntersection);
        //    if (ret) { this.Polygon = retUnion; }
        //    return ret;
        //}

        //bool AddPolygon(List<Vector2> poly)
        //{
        //    var regpoly = Vectors;
        //    if (!GeometryHelper.IsClockwisePolygon(regpoly))
        //    {
        //        regpoly.Reverse();
        //    }
        //    var addpoly = new List<Vector2>(poly);
        //    if (!GeometryHelper.IsClockwisePolygon(addpoly))
        //    {
        //        addpoly.Reverse();
        //    }

        //   // bool result = false;
        //    List<Vector2> common1 = new List<Vector2>();
        //    List<Vector2> common2 = new List<Vector2>();
        //    foreach (var vert1 in regpoly)
        //    {
        //        foreach (var vert2 in addpoly)
        //        {
        //            if (Math.Round(Vector2.Distance(vert1, vert2), 2) == 0)
        //            {
        //                common1.Add(vert1);
        //                common2.Add(vert2);
        //                //result = true;
        //            }
        //        }
        //    }
        //    if (common1.Count == 0) return false;
        //    int start1 = regpoly.IndexOf(common1.Last())-1;
        //    int end1 = regpoly.IndexOf(common1.First());
        //    //foreach (var vert in common1)
        //    //{
        //    //    start1 = Math.Min(addpoly.IndexOf(vert), start1);
        //    //    end1 = Math.Max(addpoly.IndexOf(vert), end1);
        //    //}

        //    int start2 = addpoly.IndexOf(common2.Last())+1;
        //    int end2 = addpoly.IndexOf(common2.First())+1;
        //    if (start2 > end2)
        //    {
        //        var tmp = addpoly.GetRange(start2 , addpoly.Count - start2);
        //        addpoly.RemoveRange(start2 , addpoly.Count - start2);
        //        addpoly.InsertRange(0, tmp);
        //    }

        //    regpoly.InsertRange(start1, addpoly);
        //    foreach (var vert in common1)
        //    {
        //        regpoly.Remove(vert);
        //    }

        //    //if ((start1 < end1) && (start2 < end2))
        //    //{

        //    //}
        //    //else if ((start1 < end1) && (start2 > end2))
        //    //{

        //    //}
        //    //foreach (var vert in common2)
        //    //{
        //    //    start2 = Math.Min(regpoly.IndexOf(vert), start2);
        //    //    end2 = Math.Max(regpoly.IndexOf(vert), end2);
        //    //}

        //    return true;
        //}
        public List<Vector2> Vectors
        {
            get
            {
                var vect = new List<Vector2>(polygon);
                //foreach (var pnt in polygon.Points)
                //{
                //    vect.Add(pnt);
                //}
                return vect;
            }
        }
        List<Triangle> triangles;
        public List<Vector2> Polygon
        {
            get { return polygon; }
            set
            {
                if (value != null)
                {
                    var tmp = new List<Vector2>(value);
                    if (GeometryHelper.IsClockwisePolygon(tmp))
                    {
                        tmp.Reverse();
                    }
                    try
                    {
                        triangles = Triangulation.triangulatePolygon(tmp).ToList();
                    }
                    catch (Exception ex) 
                    {
                        triangles = Triangulation.triangulatePolygon(tmp).ToList();
                    }
                    var tri = new VertexPositionColor[triangles.Count * 3];
                    for (var i = 0; i < triangles.Count; i++)
                    {
                        Color color = this.FillColor;
                        tri[i * 3] = new VertexPositionColor(new Vector3(triangles[i].v1.X,
                                                                        triangles[i].v1.Y,
                                                                        0), color);
                        tri[i * 3 + 1] = new VertexPositionColor(new Vector3(triangles[i].v2.X,
                                                                         triangles[i].v2.Y,
                                                                         0), color);
                        tri[i * 3 + 2] = new VertexPositionColor(new Vector3(triangles[i].v3.X,
                                                                         triangles[i].v3.Y,
                                                                         0), color);
                    }
                    vpc = tri;
                }
                polygon = value;
            }
        }

        public bool PointOnBorder(Vector2 pnt)
        {

            if (polygon.Count < 3) return false;
            var poly = Vectors;

            Vector2 last = poly.Last();

            foreach (var vert in poly)
            {
                if (GeometryHelper.PointIsOnLine(last, vert, pnt))
                    return true;
                last = vert;
            }
            return false;

        }
        public void SplitArea(SmoothPath sp, out List<Vector2> poly1, out List<Vector2> poly2)
        {
            poly1 = null;
            poly2 = null;

            if (sp.pathPoints.Count < 3) return;
            float len = sp.GetPathLength();
            List<Vector2> pathPoly = sp.ToPolygon((int)len / 30);//sp.ToPolygon((int)len / 30);

            if (!GeometryHelper.IsClockwisePolygon(pathPoly))
            {
                pathPoly.Reverse();
            }
            if (!PointOnBorder(pathPoly.First()))
            {
                var pnt = GetClosestPointOnAreaBorder(pathPoly.First());
                pathPoly.Insert(0, pnt);
            }
            if (!PointOnBorder(pathPoly.Last()))
            {
                var pnt = GetClosestPointOnAreaBorder(pathPoly.Last());
                pathPoly.Add(pnt);
            }
            var points = polygon;
            int start1 = 0, start2 = polygon.Count - 1;

            for (int i = 0; i < polygon.Count; i++)
            {
                start1 = i;
                if (Vector2.Distance(points[start1], pathPoly.First()) < 0.001f)
                {
                    start2 = start1;
                    pathPoly.Remove(pathPoly.First());
                    break;
                }
                if (GeometryHelper.PointIsOnLine(points[start1], points[start2], pathPoly.First()))
                {
                    break;
                }

                start2 = start1;
            }
            int end1 = 0, end2 = polygon.Count - 1;

            for (int i = 0; i < polygon.Count; i++)
            {
                end1 = i;
                if (Vector2.Distance(points[end1], pathPoly.Last()) < 0.001f)
                {
                    end2 = end1;
                    pathPoly.Remove(pathPoly.Last());
                    break;
                }
                if (GeometryHelper.PointIsOnLine(points[end1], points[end2], pathPoly.Last()))
                {
                    break;
                }

                end2 = end1;
            }

            //if (end2 == end1)
            //{

            //}

            //|| start2 == start1) throw new Exception("WTF");

            if ((start1 == end1) && (start2 == end2))
            {
                //poly1 =  new Polygon(pathPoly);
                //float dist1 = Vector2.Distance(pathPoly.First(), start1);

            }
            if (polygon.Any(x => x == pathPoly.First()))
            {
                pathPoly.Remove(pathPoly.First());
            }
            if (polygon.Any(x => x == pathPoly.Last()))
            {
                pathPoly.Remove(pathPoly.Last());
            }
            if (polygon.Contains(pathPoly.First()))
            {
                pathPoly.Remove(pathPoly.First());
            }
            if (polygon.Contains(pathPoly.Last()))
            {
                pathPoly.Remove(pathPoly.Last());
            }
            //int start, end;
            Vector2[] first;

            {
                int cnt;
                if ((start1 == end1) && (start2 == end2))
                {
                    cnt = polygon.Count + pathPoly.Count;
                }
                else
                {
                    cnt = (end2 + (end2 < start1 ? polygon.Count : 0)) - start1 + 1 + pathPoly.Count;
                }
                first = new Vector2[cnt];

                var arr = points.ToArray();
                int k = 0;


                while (k < pathPoly.Count)
                {
                    first[k] = pathPoly[pathPoly.Count - k - 1];
                    k++;
                }
                int i = start1;
                while (k < first.Count())
                {

                    first[k] = new Vector2(arr[i].X, arr[i].Y);
                    i++;
                    if (i == arr.Length) { i = 0; }
                    k++;
                }
            }



            Vector2[] second;

            {
                int cnt;
                if ((start1 == end1) && (start2 == end2))
                {
                    cnt = pathPoly.Count;
                }
                else
                {
                    cnt = (start2 + (end1 > start2 ? polygon.Count : 0)) - end1 + 1 + pathPoly.Count;
                }
                if ((start1 == end1) && (start2 == end2))
                {
                    //poly1 =  new Polygon(pathPoly);
                    //float dist1 = Vector2.Distance(pathPoly.First(), start1);
                    cnt = pathPoly.Count;
                }

                second = new Vector2[cnt];

                var arr = points.ToArray();
                int k = 0;


                while (k < pathPoly.Count)
                {
                    second[k] = pathPoly[k];
                    k++;
                }
                int i = end1;
                while (k < second.Count())
                {

                    second[k] = new Vector2(arr[i].X, arr[i].Y);
                    i++;
                    if (i == arr.Length) { i = 0; }
                    k++;
                }



            }

            poly1 = first.ToList();
            poly2 = second.ToList();
        }
        public Vector2? GetTrajectoryIntersection(Vector2 start, Vector2 end)
        {
            if (polygon.Count == 0) return null;
            var poly = Vectors;
            Vector2? retPnt = null;
            Vector2 last = poly.Last();
            var distance = 20000f;
            foreach (var vert in poly)
            {
                var pnt = GeometryHelper.GetTrajectoryIntersectionOnLineDouble(last, vert, start, end);
                if (pnt != null)
                {
                    var tmpdist = Vector2.Distance(end, pnt.Value);
                    if (tmpdist < distance) { distance = tmpdist; retPnt = pnt.Value; }
                }
                last = vert;
            }
            return retPnt;
        }

        public Vector2 GetClosestPointOnAreaBorder(Vector2 P)
        {
            //if (polygon.Count == 0) return -Vector2.One;
            //var poly = Vectors;
            //Vector2 retPnt = -Vector2.One;
            //Vector2 last = poly.Last();
            //var distance = Vector2.Distance(P, poly.First());
            //foreach (var vert in poly)
            //{
            //    var pnt = GeometryHelper.GetClosestPointOnLine(last, vert, P);
            //    var tmpdist = Vector2.Distance(P, pnt);
            //    if (tmpdist < distance) { distance = tmpdist; retPnt = pnt; }
            //    last = vert;
            //}
            //return retPnt;
            return GeometryHelper.GetClosestPointOnAreaBorder(P, Vectors);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            try
            {

                var gd = spriteBatch.GraphicsDevice;
                BasicEffect effect = new BasicEffect(gd);

                effect.Projection = Matrix.CreateOrthographicOffCenter
                    (0, gd.Viewport.Width,
                    gd.Viewport.Height, 0,
                    0, 1);
                effect.VertexColorEnabled = true;

                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    spriteBatch.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, vpc, 0, triangles.Count, VertexPositionColor.VertexDeclaration);


                }


            }
            catch { }

        }
        public bool HitTest(Vector2 pnt)
        {

            if (polygon.Count < 3) return false;

            Vector2 last = polygon.Last();
            bool c = false;
            foreach (var vert in polygon)
            {
                if (((Math.Round(vert.Y - pnt.Y, 2) >= 0) != (Math.Round(last.Y - pnt.Y, 2) >= 0)) && (Math.Round(pnt.X - ((last.X - vert.X) * (pnt.Y - vert.Y) / (last.Y - vert.Y) + vert.X), 2) <= 0))
                    c = !c;
                last = vert;
            }


            return c;

        }

        public Vector2 getPointNormalOffset(Vector2 point, float offset)
        {
            if (!PointOnBorder(point))
            {
                point = GetClosestPointOnAreaBorder(point);
            }


            var poly = Vectors;

            Vector2 last = poly.Last();

            foreach (var vert in poly)
            {
                if (GeometryHelper.PointIsOnLine(last, vert, point))
                {

                    Vector2 pnt = (vert - last);
                    var rot = (float)Math.Atan2(pnt.X, -pnt.Y) - MathHelper.PiOver2;


                    return new Vector2(point.X + offset * (float)Math.Sin(rot), point.Y - offset * (float)Math.Cos(rot));

                    //Vector2 vect = vert-last*;
                    //var rot = (float)Math.Atan2(vect.Y, vect.X);


                    //new Vector2(vect.X + 40 * (float)Math.Sin(rot), vect.Y - 40 * (float)Math.Cos(rot))

                    //tween_position = Vector2.Transform(tween_position, m);
                }
                last = vert;
            }
            return -Vector2.One;
        }
        public void CreateHole(List<Vector2> hole)
        {
            var polyA = Polygon;
            var polyB = hole;
            if (polyA.Count == 0 || polyB.Count == 0) return;
            //var poly = Vectors;
            Vector2 retPnt = -Vector2.One;
            Vector2 polyPnt = -Vector2.One;
            Vector2 last = polyB.Last();
            var distance = 20000f;// Vector2.Distance(P, poly.First());
            foreach (var vert in polyB)
            {
                var pnt = GeometryHelper.GetClosestPointOnAreaBorder(vert, polyA);
                var tmpdist = Vector2.Distance(vert, pnt);
                if (tmpdist < distance)
                {

                    distance = tmpdist;
                    retPnt = vert;
                    polyPnt = pnt;
                }

                last = vert;
            }
            var Check_distance = distance;
            var Check_polyPnt = polyPnt;
            var Check_retPnt = retPnt;

            polyA = hole;
            polyB = Polygon;

            retPnt = -Vector2.One;
            polyPnt = -Vector2.One;
            last = polyB.Last();
            distance = 20000f;// Vector2.Distance(P, poly.First());
            foreach (var vert in polyB)
            {
                var pnt = GeometryHelper.GetClosestPointOnAreaBorder(vert, polyA);
                var tmpdist = Vector2.Distance(vert, pnt);
                if (tmpdist < distance)
                {

                    distance = tmpdist;
                    retPnt = vert;
                    polyPnt = pnt;
                }

                last = vert;
            }

            if (distance < Check_distance)
            {
                //distance = tmpdist;
                //retPnt = vert;
                //polyPnt = pnt;
            }
            else
            {
                polyA = Polygon;
                polyB = hole;
                distance = Check_distance;
                retPnt = Check_retPnt;
                polyPnt = Check_polyPnt;
            }

            int ind = polyB.IndexOf(retPnt);

            last = polyA.Last();
            //var distance = Vector2.Distance(P, poly.First());
            List<Vector2> poly3 = new List<Vector2>();
            foreach (var vert in polyA)
            {
                var pnt = GeometryHelper.GetClosestPointOnLine(last, vert, retPnt);
                if (pnt == polyPnt)
                {


                    poly3 = new List<Vector2>(polyA);
                    var vrt = vert;
                    if (pnt == last) vrt = last;
                    int ind2 = poly3.IndexOf(vrt) + 1;
                    if (poly3.Contains(pnt))
                    {

                        poly3.Insert(poly3.IndexOf(vrt), pnt);
                    }
                    else
                    {
                        poly3.Insert(poly3.IndexOf(vrt), pnt);
                        poly3.Insert(poly3.IndexOf(vrt), pnt);
                    }

                    poly3.Insert(ind2, retPnt);
                    poly3.InsertRange(ind2, polyB.GetRange(0, ind));
                    poly3.InsertRange(ind2, polyB.GetRange(ind, polyB.Count - ind));



                    Polygon = poly3;
                    return;
                    //break;
                }

                last = vert;
            }

            if (poly3 == null)
            {
                throw new Exception("WTF");
            }
        }
    }
}
